#include "crossbit/ExecutionEngine.h"
#include "crossbit/Exit.h"
#include "crossbit/Profiler.h"
#include "vector"
#include "crossbit/RBThread.h"

using namespace crossbit;

//#define DEBUG
//#define LINK

extern RBFuncIndx RBFuncTable[RBFUNCNUM];

void myFree(void *ptr)
{
	printf("In myFree: ptr %x\n", ptr);
	//free(ptr);
}

void ExecutionEngine::run()
{
	ee_memory_image->load();
	ee_interpreter->initStack( ee_memory_image->stack() );

	XTMemAddr entry = ee_memory_image->entry();

	TBlock* pre_tblock = NULL;
	VBlock* pre_vblock = NULL;
	GLOBAL::BUILD_SUPER_BLOCK = 0;

/*	
	XTMemAddr symb_addr = NULL;
	char *funcname = "clone";
	if(symb_addr = ee_memory_image->lookupFuncSPC(funcname)) {
			printf("symb_addr = %x\n", symb_addr);
			TBlock *tmp = ee_tcache -> requestTBlock();	
			ee_translator -> encodeLibcFuncWrapper((XTMemAddr)&myFree, tmp, 1);   
			ee_tcache -> submitTBlock(symb_addr,tmp,NULL);
		}
*/

	XTMemAddr symb_addr = NULL;
		for(int i=0; i<RBFUNCNUM; i++){
			if(symb_addr = ee_memory_image->lookupFuncSPC(RBFuncTable[i].ori_func_name)) {
				TBlock *tmp = ee_tcache -> requestTBlock();	
				ee_translator -> encodeLibcFuncWrapper(RBFuncTable[i].rb_func_addr, tmp, RBFuncTable[i].ori_arg_num);   
				ee_tcache -> submitTBlock(symb_addr,tmp,NULL);
			}
		}


	while (1) {

#ifdef DEBUG
		std::cout<<"entry address:"<<std::hex<<"0X"<<entry<<std::endl;
		XTInt32 stop = 1;
	if( entry == 0x41738c ) while(stop);
		std::cout<<"0x3fffc014:"<<std::hex<<"0X"<<*(XTUint32*)(0x3fffc014)<<std::endl;
		std::cout<<"0x3fffc0c4:"<<(char*)(0X3fffc0c4)<<std::endl;
		std::cout<<"0x3fffc0c6:"<<(char*)(0X3fffc0c6)<<std::endl;
#endif
//printf("\nThe entry is 0x%x\n",entry);
		TBlock *tblock = ee_tcache -> lookupTBlock(entry);
		VBlock *vblock;
		if (tblock == NULL) {
			ee_builder->getReady(entry);
			XTMemAddr next = entry;
			while( !ee_builder->isEnd() )
				next = ee_interpreter -> decode(next, ee_builder);
			vblock = ee_builder -> wrapUp(next);
			tblock = ee_tcache -> requestTBlock();
			ee_translator -> encode(vblock, tblock);    
			ee_tcache -> submitTBlock(entry,tblock,vblock);
		}

		XTMemAddr enter = tblock->enterAddr();
#ifdef DEBUG

		if( enter == 0xa0000df7 )
			write(10, (void*)enter, 300);

		std::cout<<"enter address:"<<std::hex<<"0X"<<enter<<std::endl;
		ee_interpreter->interp_state->dump();
#endif

#ifdef LINK

		if( pre_tblock != NULL ){
			Exit exit = pre_tblock ->getExit(GLOBAL::last_exit);

			pre_vblock = ee_tcache -> lookupVBlock( pre_tblock -> getSPC() ); 
			if( exit.link_type == LinkType::DIRECT ){
				ee_trace_manager -> directLinkTo( pre_tblock , GLOBAL::last_exit , tblock );
			}else if( exit.link_type == LinkType::INDIRECT ){
				ee_trace_manager -> indirectLinkTo( pre_tblock , GLOBAL::last_exit , tblock );
			}
		}
#endif
//printf("The Tblock begins to run!!\n");
		CONTEXT_SAVE
			((void (*)()) enter) ();
		CONTEXT_RESTORE

//printf("The Tblock ends to run!\n");
			if( GLOBAL::BUILD_SUPER_BLOCK ){
				GLOBAL::BUILD_SUPER_BLOCK = 0;
				entry = GLOBAL::tblock_stub -> getSPC();
				TBlock* former_tblock = ee_tcache -> lookupTBlock( entry );
				VBlock *super_vblock = Profiler::buildSuperVBlock( ee_tcache , entry ); 	
				if( super_vblock == NULL ){
					super_vblock = ee_tcache -> lookupVBlock( entry );
				}
				TBlock *super_tblock = ee_tcache -> requestTBlock();
				super_tblock -> setSuperBlock();
				ee_translator -> encode( super_vblock , super_tblock ); 	

				ee_tcache -> submitTBlock( entry , super_tblock , super_vblock );						
				ee_trace_manager -> linkSuperBlock( former_tblock , super_tblock );
				pre_tblock = NULL;	
				continue;
			}


		Exit exit = GLOBAL::tblock_stub->getExit(GLOBAL::last_exit);
		if (exit.why == ExitReason::SYSCALLEXIT) {
			(*ee_syscall_handler)();
		}

		entry = ee_rb->RBSchedule(exit.next, entry);

		pre_tblock = GLOBAL::tblock_stub;
	}

}

